闲话 22.11.25

闲话

完了下面杂题的字号太大了
这段闲话似乎需要在调小下面字号的情况下写(

不知道为什么感觉这边需要变一个画风
?中午还在想写啥 下午就不知道该从何说起了
但一般而言从不知从何说起说起是比较不错的开头
因为我想扯闲天 所以没什么关系

旁边有人问我想干啥
我说我想扯闲天 然后敲完了上面的话

最近感觉上入冬了(
晚上去吃饭的路上裹着羽绒服 紧紧揣着兜
然后看着路灯
总感觉这种感觉之前有过
那时候似乎在下雪
然后就吐槽为什么不下雪
然后被吐槽了白色相簿的季节
是想要的吐槽(

嘛感觉我这边大概上不会有什么心态上的变化
有也不会太大
反正生活都要继续嘛
完了开始沉重了
那就扯开吧

某人看了一点小圆剧场版
然后跟我剧透某人变魔女了
我的回答:肯定是知道内情的人才会出现这种情况,所以是qb(
“你说得对”

昨天为什么没有闲话?
打 mc 打得很嗨
今天为什么有闲话?
不让打 mc 了

感觉上如果缺了闲话会在摘要里补上
嘿打完刚才那句话自动补上了个句号
然后觉得不是很对劲给他删了
就这种写法我是比较喜欢的(
Why so serious?

这种日常感是很重要的呢!
感觉上我最重视的永远是日常感
尤其是在对作品的选择上
小圆这种显然就不在其中了(

似乎写完了
所以下面的标题字号可以调回来了(
草生 没调过来
现在好了 如果有人刚才看到了小小的标题就知道我说的是真的了(

仍然是惯例的杂题!

进行一个闲话的补

[POI2012]PRE-Prefixuffix

对于两个串S1、S2,如果能够将S1的一个后缀移动到开头后变成S2,就称S1和S2循环相同。例如串ababba和串abbaab是循环相同的。
给出一个长度为n的串S,求满足下面条件的最大的L:

  1. Ln2
  2. S的L前缀和S的L后缀是循环相同的。

1n106

考虑一个串是满足条件的当且仅当其形如 ABCBA,其中 L=AB。容易发现我们需要迅速求出删掉原串两侧左右两侧 border 后(BCB)的新最长 border 长度。如果这玩意是道子串查询 border 长度的题就没意思了。而且不迅速(

考虑我们需要求的是删除原串两侧定长度子串后剩余串的 border。这玩意可以递推。
fi 为原串两侧删除 i 长度子串后串的 border 长度。初值为 fn2=0 因为串没了。
每一次向左右两侧拓展一个字符,border 长度最长增加 2。朴素地验证当前长度(fi=fi+1+2)是否是一个 border 的长度,不满足就自减 1 后接着查询。
使用哈希做到 O(n) dp 出每个位置的 border 长度。
随后找到原串所有 border 就做完了。

总时间复杂度 O(n)

code
#include <bits/stdc++.h>
#define rep(i,s,t) for (int i = (s), i##END = (t) + 1; i < i##END; ++ i)
#define pre(i,s,t) for (int i = (s), i##END = (t) - 1; i > i##END; -- i)
using namespace std;
const int N = 1e6 + 10, bse = 131, mod = 1e9 + 3579;
int n, ans, nxt[N], f[N], hsh[N], pw[N];
char ch[N];

int get(int l, int r) { return (1ll * hsh[r] - 1ll * hsh[l - 1] * pw[r - l + 1] % mod + mod) % mod; }

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> ch + 1; pw[0] = 1;
    rep(i,1,n) hsh[i] = (1ll * hsh[i - 1] * bse + ch[i]) % mod, pw[i] = 1ll * pw[i - 1] * bse % mod;
    for (int i = 2, j = 0; i <= n; ++ i) {
        while (j and ch[j + 1] != ch[i]) j = nxt[j];
        if (ch[j + 1] == ch[i]) ++ j;
        nxt[i] = j;
    } 
    int st = (n >> 1) + (n & 1);
    f[st] = 0;
    pre(i, st-1, 0) {
        f[i] = min(f[i + 1] + 2, (n - (i << 1)) >> 1);
        while (f[i] and get(i + 1, i + f[i]) != get(n - i - f[i] + 1, n - i)) -- f[i];
    }
    for (int p = nxt[n]; p; p = nxt[p]) if (p < st) ans = max(ans, p + f[p]);
    cout << ans << '\n';
}



CF961F

给定一个长度为 n 的字符串 T 。定义 k 子串表示 Sk,Sk+1 Snk+1 。显然的, 1 子串= T,并且有 n2k 子串。

对于每一个 k 子串 k=1,2,3...n2 ,试找出最大长度的字符串 t,使得 tT 的前缀和后缀且 t 的 长度为奇数。

2n106

和上面那题的做法一样。
初值看着设成 1/-1,然后每次自增 2。验证当前情况下如果不满足条件就自减 2,直到减成 - 1。
这题 *2700 不是很认可。

code
#include <bits/stdc++.h>
#define rep(i,s,t) for (int i = (s), i##END = (t) + 1; i < i##END; ++ i)
#define pre(i,s,t) for (int i = (s), i##END = (t) - 1; i > i##END; -- i)
using namespace std;
const int N = 1e6 + 10, bse = 131, mod = 1e9 + 3579;
int n, ans, f[N], hsh[N], pw[N];
char ch[N];
int get(int l, int r) { return (1ll * hsh[r] - 1ll * hsh[l - 1] * pw[r - l + 1] % mod + mod) % mod; }

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> ch + 1; pw[0] = 1;
    rep(i,1,n) hsh[i] = (1ll * hsh[i - 1] * bse + ch[i]) % mod, pw[i] = 1ll * pw[i - 1] * bse % mod;
    int st = (n >> 1) + (n & 1);
    if (n & 1) f[st] = -1;
    else f[st] = (ch[st] == ch[st + 1] ? 1 : -1); 
    pre(i, st-1, 0) {
        f[i] = f[i + 1] + 2; 
        while (f[i] > -1 and get(i, i + f[i] - 1) != get(n - i + 1 - f[i] + 1, n - i + 1)) f[i] -= 2;
    } rep(i,1,st) cout << f[i] << ' ';
}



CF961E

给定长度为 n 的序列 a,求满足 1i<jnaijaji 的二元组 (i,j) 的个数。

1n2×1051ai109

没错,做完上面那题之后看到的。

考虑怎么维护三个性质。
我们一点点删掉不满足条件的 i,最开始 j=n,所有 i 都满足条件。
a 先排个序,然后 j 倒着扫统计。首先把所有满足 ai<ji 都扔掉。然后查询小于等于 min(aj,j1)i 的个数作为答案。

然后做完了。总时间复杂度 O(nlogn)

*1900 正常。紫色的不是很认可。

code
#include <bits/stdc++.h>
#define rep(i,s,t) for (int i = (s), i##END = (t) + 1; i < i##END; ++ i)
#define pre(i,s,t) for (int i = (s), i##END = (t) - 1; i > i##END; -- i)
using namespace std;
const int N = 2e5 + 10;
int n, a[N], id[N];
long long ans;

struct BIT {
    int Index[N];
    void add(int p, int v) { for (; p <= n; p += p & -p) Index[p] += v; }
    int qry(int p) { int ret = 0; for (; p; p ^= p & -p) ret += Index[p]; return ret; }
} B;

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n; rep(i,1,n) cin >> a[i], id[i] = i;
    sort(id + 1, id + 1 + n, [&](const int &x, const int &y){ return a[x] < a[y]; });
    int ptr = 1;
    rep(i,1,n) B.Index[i] = (i & -i);
    rep(j,1,n) {
        while (ptr <= n and a[id[ptr]] < j) B.add(id[ptr], -1), ++ ptr;
        ans += B.qry(min(j - 1, a[j])); 
    } cout << ans << '\n';
}



P7728 旧神归来

题面长,不搬。

首先很容易发现一个东西
就是你应该用生成函数去刻画这个东西(
而且我们只需要去刻画这个叶子

S(Ti) 为树 Ti 上的叶子组成的多重集对应的生成函数。具体地,

S(Ti)=i=1[ i ]xi

然后记 minf(f(x)) 为一个多项式 f(x) 中最低次项。minf(x3+3x)=x 这种样子的。

然后可以刻画了。

S(Ti+1)=S(Ti)×minf(S(Ti))+S(Ti)minf(S(Ti))=S(Ti)×(1+minf(S(Ti)))minf(S(Ti))

挺长的

然后我们观察两次合并会产生些什么效果。
为了不让式子变长,我们记第一次换的叶子是 xd,第二次换的叶子是 xe
那两次换叶子的功效就是

(S(Ti)×(1+xd)xd)×(1+xe)xe=S(Ti)(1+xd)(1+xe)xd+exdxe=(S(Ti)1)(1+xd)(1+xe)+1

挺好看

这告诉我们其实两次换叶子是等价的,顺序不重要。

然后我们假设深度为 i 的叶子被换了 ai 次后换完。

(S(T0)1)×i1(1+xi)ai+1=0

然后就是

i1(1+xi)ai=11S(T0)

看到 取对数:

i1ailn(1+xi)=i1j1(1)jaijxij=ln11S(T0)

对比系数得到

ij=n(1)j+1aij=[xn]ln11S(T0)

注意这里是 xij 次方,而不是 xij 次方。因此最后 1 的指数是 j+1

an=[xn]ln11S(T0)+ij=nin(1)jaij

做完了。总时间复杂度 O(nlogn)
还没实现。晚点再说。

upd: 实现了 写的是枚举因子的写法所以是 O(nn)
注意枚举因子时 i×i=n 时不要统计两次。

code
#include <bits/stdc++.h>
#define rep(i,s,t) for (int i = (s), i##END = (t) + 1; i < i##END; ++ i)
#define pre(i,s,t) for (int i = (s), i##END = (t) - 1; i > i##END; -- i)
using namespace std;
int n, m, t1, t2, ans[N];
unsigned a[N];
vector<int> g[N];

void dfs(int u, int fa, int dep) {
    if (g[u].size() == 1) a[dep] = (1ll * a[dep] - 1 + mod) % mod;
    for (auto v : g[u]) if (v != fa) dfs(v, u, dep + 1);
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    rep(i,2,n) cin >> t1 >> t2, g[t1].emplace_back(t2), g[t2].emplace_back(t1);
    dfs(1, 0, 0); a[0] ++;
    poly f(a, a + m + 1);
    f = f.ln();
    rep(i,1,m) f[i] = (mod - 1ll * f[i]) % mod;
    rep(k,1,m) {
        ans[k] = f[k];
        for (int i = 1, j; i * i <= k; ++ i) if (k % i == 0) {
            j = k / i;
            if (i != k) ans[k] = (ans[k] + 1ll * ( (j & 1) ? mod - 1 : 1 ) * ans[i] % mod * math::ginv(j)) % mod;
            if (i * i != k) {
                swap(i, j);
                if (i != k) ans[k] = (ans[k] + 1ll * ( (j & 1) ? mod - 1 : 1 ) * ans[i] % mod * math::ginv(j)) % mod;
                swap(i, j);
            }
        }
    } 
    rep(i,1,m) ans[i] = (ans[i - 1] + ans[i]) % mod;
    rep(i,1,m) cout << ans[i] << '\n';
}
posted @   joke3579  阅读(137)  评论(8编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示